package org.dawfinn.droiditarian;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.dawfinn.droiditarian.data.DeedLocation;
import org.dawfinn.droiditarian.deedlocationsource.InternetDeedLocationSource;

import android.graphics.drawable.Drawable;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Overlay;

public class DeedMap extends MapActivity implements LocationListener {
	private static final String GET_LOCATIONS_PATH = "http://droiditarian.appspot.com/" 
		+ "locations";

	private static final double GEO_CONVERSION = 1E6;
	private static final int LAT_MAX = 90;
	private static final int LNG_MAX = 180;
	private static final int LAT_RANGE_BUFFER = 1500;
	private static final int LNG_RANGE_BUFFER = 3000;

	private MapView map;
	private LocationManager lm;
	private MyLocationOverlay myLocationOverlay;
	private Drawable marker;
	private boolean shouldZoom;
	
	private List<DeedLocation> deedLocs;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.map_view);
		lm = (LocationManager) getSystemService(LOCATION_SERVICE);
		deedLocs = new ArrayList<DeedLocation>();
		setupMap();
		setupOverlays();
		// We want to set zoom onCreate
		shouldZoom = true;
	}

	@Override
	protected void onPause() {
		super.onPause();
		lm.removeUpdates(this);
		myLocationOverlay.disableMyLocation();
		shouldZoom = false;
	}

	@Override
	protected void onResume() {
		super.onResume();
		if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER))
			lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000 * 5, (float) 0.1, this);
		else if (lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
			lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000 * 5, (float) 0.1, this);
		new GetLocationsAsyncTask().execute();
		myLocationOverlay.enableMyLocation();
	}

	private void setupMap() {
		map = (MapView) findViewById(R.id.mapview);
		map.setSatellite(true);
		map.setBuiltInZoomControls(true);
	}

	/**
	 * Sets the zoom level to be so that it just shows the most recent deeds,
	 * and the center to be in the middle of the deeds
	 */
	private void setZoomCenterAndLevel() {
		MapController mapController = map.getController();

		double minLat = LAT_MAX;
		double maxLat = -LAT_MAX;
		double minLng = LNG_MAX;
		double maxLng = -LNG_MAX;
		for (DeedLocation loc : deedLocs) {
			double lat = loc.getLatitude();
			double lng = loc.getLongitude();
			minLat = Math.min(lat, minLat);
			minLng = Math.min(lng, minLng);
			maxLat = Math.max(lat, maxLat);
			maxLng = Math.max(lng, maxLng);
		}
		GeoPoint myLoc = getMyLocation();
		int myLat = myLoc.getLatitudeE6();
		int myLng = myLoc.getLongitudeE6();
		int latRange = Math.max(Math.abs(myLat - (int)(minLat*GEO_CONVERSION)),
				Math.abs(myLat - (int)(maxLat*GEO_CONVERSION)));
		int lngRange = Math.max(Math.abs(myLng - (int)(minLng*GEO_CONVERSION)),
				Math.abs(myLng - (int)(maxLng*GEO_CONVERSION)));
		// Set the center to be in the middle of the lat and long points of the recent deeds
		mapController.animateTo(myLoc);
		// Set the zoom to span the range of the lat and long points of the recent deeds
		mapController.zoomToSpan((latRange*2) + LAT_RANGE_BUFFER, (lngRange*2) + LNG_RANGE_BUFFER);
	}
	
	private GeoPoint getMyLocation() {

		Criteria criteria = new Criteria();
		criteria.setAccuracy(Criteria.ACCURACY_COARSE);
		criteria.setAltitudeRequired(false);
		criteria.setBearingRequired(false);
		criteria.setCostAllowed(true);
		criteria.setPowerRequirement(Criteria.POWER_LOW);
		Location location = lm.getLastKnownLocation(lm.getBestProvider(criteria, false));
		if (location != null) {
			return new GeoPoint((int)(location.getLatitude()*GEO_CONVERSION),
					(int)(location.getLongitude()*GEO_CONVERSION));
		} else {
			return new GeoPoint(0, 0);
		}
	}

	private void setupOverlays() {
		marker = getResources().getDrawable(R.drawable.little_map_dude);
		marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight());
		
		myLocationOverlay = new MyLocationOverlay(this, map);
		
		List<Overlay> overlays = map.getOverlays();
		overlays.add(myLocationOverlay);
		overlays.add(new MapOverlay(marker, this, deedLocs));
	}
	
	private List<DeedLocation> getDeedLocations() {
		try {
			// The next line is for testing only.
			// List<DeedLocation> deedLocs = new LocalDeedLocationSource().getDeedLocations();
			
			return new InternetDeedLocationSource(GET_LOCATIONS_PATH)
				.getDeedLocations();
		} catch (IOException e) {
			Log.e("MapOverlay", e.getMessage());
			return (deedLocs == null) ? new ArrayList<DeedLocation>() : deedLocs;
		}
	}

	protected boolean isRouteDisplayed() {
		return false;
	}

	public void onLocationChanged(Location arg0) {
		/*GeoPoint myLocation = new GeoPoint((int) (arg0.getLatitude() * 1E6), (int) (arg0.getLongitude() * 1E6));
		map.getController().animateTo(myLocation);*/
	}

	public void onProviderDisabled(String arg0) {
	}

	public void onProviderEnabled(String arg0) {
	}

	public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
	}
	
	class GetLocationsAsyncTask extends AsyncTask<Void, Void, List<DeedLocation>> {
		@Override
		protected List<DeedLocation> doInBackground(Void... arg0) {
			return getDeedLocations();
		}
		
		protected void onPostExecute(List<DeedLocation> result) {
			deedLocs = result;
			map.getOverlays().set(1, new MapOverlay(marker, DeedMap.this, deedLocs));
			if (shouldZoom) {
				setZoomCenterAndLevel();
			}
	    }
	}
}
